{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "initial_id",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-11-21T13:31:25.022339600Z",
     "start_time": "2023-11-21T13:31:25.016155200Z"
    }
   },
   "outputs": [],
   "source": [
    "from advisor_backend.interface import Interface\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "57d17a21205c3c5e",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "# 集群调优分析\n",
    "## 1. 集群分析的数据准备\n",
    "首先我们当前支持PyTorch多卡大模型的集群分析，您需要输入集群分析的profiling_path路径，例如：\n",
    "--{profiling_path}\n",
    "    -- xxxx_ascend_pt\n",
    "    -- xxxx_ascend_pt\n",
    "    -- xxxx_ascend_pt\n",
    "    ......\n",
    "    -- xxxx_ascend_pt\n",
    "里面每张卡的profiling文件都是ascend_pt结尾的文件。\n",
    "\n",
    "## 2. 集群分析解决的问题\n",
    "当前的功能主要有三项：\n",
    "1）. 识别多卡间的计算慢卡（根据计算时间等推断）\n",
    "2）. 识别多卡间的通信慢现象（根据通信链路的带宽判断）\n",
    "3）. 对多卡间的计算算子进行统计展示（识别不同卡的算子差异）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "36b7a24cc7ca5da2",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-11-21T12:53:38.379699800Z",
     "start_time": "2023-11-21T12:53:38.363755900Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [],
   "source": [
    "# EDIT THE PROFILING DATA PATH\n",
    "cluster_path = \"YOUR PATH\"\n",
    "interface = Interface(cluster_path)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cf832ac2e0dfa30f",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "## 1) 识别慢卡"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "40aac93278dd6e34",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2023-11-21T12:53:41.815599700Z",
     "start_time": "2023-11-21T12:53:41.783393700Z"
    },
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[INFO]Cluster has been analyzed because of the existence of cluster analysis output directory.\n",
      "[INFO]Skip Cluster analyze backend.\n"
     ]
    }
   ],
   "source": [
    "dataset = interface.get_data('cluster', 'slow rank')\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "cd3fceda-49f0-439f-9c54-cc31490fc99e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# EDIT THE DATA TO SHOW WHAT YOU WANT\n",
    "data = dataset.get('data')\n",
    "words = dataset.get('bottleneck')\n",
    "rank_ids = list(data.keys())\n",
    "# 柱状图显示属性\n",
    "compute_time = [data.get(key, {})[0] for key in rank_ids]\n",
    "communication_time = [data.get(key, {})[1] for key in rank_ids]\n",
    "free_time = [data.get(key, {})[2] for key in rank_ids]\n",
    "# 柱宽\n",
    "width = 0.2\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "6a1d82fb-a31b-49ab-a859-6d4bb898c512",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Communication has some issues in the cluster, because the max difference of Communication time has reached 88.476ms. \n",
      "Free has some issues in the cluster, because the max difference of Free time has reached 29.224ms. \n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "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OdWpU0fLli1zyrNs2TLVqVNHXl5eatWqlebPn3/B8QEAAICCorDhiqSlpWnFihWKjY296OmD/v7+stvt6tixo9LS0rRmzRolJCTop59+Uvfu3Z3G7t+/X59//rlWrFih9957T2+//bbat2+vX375RWvWrNHkyZM1atQobdy40el5zz33nDp37qzvvvtOvXr1Uo8ePbRr164C5W/RooWmTp0qX19fpaSkKCUlRUOHDs1z/Lhx49StWzd9//33uvfee9WrVy+lpaVJkg4cOKAuXbqoU6dO+u677/T444/r2WefLVAOAAAA4GIobLgi+/btk2VZqlevXp5jEhMTtX37di1cuFBNmjRRRESEFixYoDVr1mjz5s2OcXa7XXPmzFGDBg3UoUMHtWrVSrt379bUqVNVt25d9enTR3Xr1tWqVauc9t+1a1c9+uijuuGGGzRhwgQ1bdpUr732WoHye3h4yM/PTzabTUFBQQoKClL58uXzHN+7d2/17NlTtWvX1gsvvKBTp05p06ZNkqQ33nhDdevW1UsvvaS6deuqR48e6t27d4FyAAAAABdDYcMVsSwr3zG7du1SaGioQkNDHesaNGggf39/p5mwmjVrqkKFCo7lwMBANWjQwOnLxAMDA3Xs2DGn/UdGRl6wXNAZtsJq1KiR488+Pj7y9fV15Nm9e7eaNWvmNL558+ZXJQcAAABKBwobrkidOnVks9mK5MYiZcuWdVq22WwXXWe32wu8z3Nl7/ximZOTU6QZC5MHAAAAKAwKG65IxYoVFR0drZkzZyorK+uC7enp6apfv74OHz6sw4cPO9b/8MMPSk9PV4MGDa44w7kbm5y/XL9+fUn/f6fHlJQUx/bk5GSn8R4eHsrNzb3iHHXr1tWWLVuc1p1/yicAAABQWBQ2XLGZM2cqNzdXzZs310cffaS9e/dq165dmj59uiIjIxUVFaXw8HD16tVLSUlJ2rRpkx555BHdddddatq06RUff/HixZozZ4727NmjMWPGaNOmTYqLi5Mk1a5dW6GhoRo7dqz27t2rTz/9VK+88orT82vWrKlTp04pMTFRJ06c0OnTpy8rx+OPP64ff/xRI0aM0J49e/TBBx9o3rx5kv6aiQMAAAAKi+9hM9zlfpF1capVq5aSkpI0ceJEPf3000pJSVGVKlXUpEkTzZo1SzabTZ988okGDBigO++8U25ubmrbtm2BbwySn3Hjxun999/Xk08+qeDgYL333nuOmbuyZcvqvffe0xNPPKFGjRqpWbNmev7559W1a1fH81u0aKF//vOf6t69u3777TeNGTPGcWv/wggLC9OHH36op59+WtOmTVNkZKSeffZZPfHEE/L09CyS1woAAIDSxWYV5K4RKBKZmZny8/NTRkaGfH19nbadOXNGBw4cUFhYmLy8vFyUsOSx2WxasmSJOnXq5OooFzVx4kTNnj3b6XRQ0/CzBwAAUPwu1Q3OxwwbUIRef/11NWvWTJUqVdLatWv10ksvOU7PBAAAAAqLwgYUob179+r5559XWlqaqlevrqefflojR450dSwAAACUUBQ2lGimndH76quv6tVXX3V1DAAAAFwjuEskAAAAABiKGTYAAIBr0Vi/AozJuPo5AFwRZtgAAAAAwFAUNgAAAAAwFIUNAAAAAAxFYQMAAAAAQ3HTEdMV5ILhIj0eFx8XlM1m05IlS9SpU6erdox58+Zp0KBBSk9Pv2rHAAAAgLmYYUORSE1N1YABA1SrVi15enoqNDRUHTp0UGJioqujXTUpKSlq165dke2vZs2amjp1qtO67t27a8+ePUV2DAAAAJQszLDhih08eFC33Xab/P399dJLLyk8PFw5OTlauXKlYmNj9eOPP7o64lURFBR01Y/h7e0tb2/vq34cAAAAmIkZNlyxJ598UjabTZs2bVLnzp11ww03qGHDhhoyZIg2bNggSTp06JA6duyo8uXLy9fXV926ddPRo0cd+xg7dqxuuukmzZkzR9WrV1f58uX15JNPKjc3V/Hx8QoKClJAQIAmTpzodGybzaY33nhD//jHP1SuXDnVr19f69ev1759+9SyZUv5+PioRYsW2r9/v+M5vXv3vuA0xkGDBqlly5aO5ZYtW+qpp57S8OHDVbFiRQUFBWns2LEXHHvp0qWO5V9++UU9e/ZUxYoV5ePjo6ZNm2rjxo2SpP3796tjx44KDAxU+fLl1axZM3355ZdOx/v55581ePBg2Ww22Ww2SX+dEunv7+903FmzZun666+Xh4eH6tatq3feeeeCXG+99Zbuv/9+lStXTnXq1NGyZcvy/g8IAAAAY1HYcEXS0tK0YsUKxcbGysfH54Lt/v7+stvt6tixo9LS0rRmzRolJCTop59+Uvfu3Z3G7t+/X59//rlWrFih9957T2+//bbat2+vX375RWvWrNHkyZM1atQoRwk6Z8KECXrkkUeUnJysevXq6cEHH9Tjjz+ukSNHasuWLbIsS3FxcYV+bfPnz5ePj482btyo+Ph4jR8/XgkJCRcde+rUKd111106cuSIli1bpu+++07Dhw+X3W53bL/33nuVmJiobdu2qW3bturQoYMOHTokSfr4449VrVo1jR8/XikpKUpJSbnocZYsWaKBAwfq6aef1o4dO/T444+rT58+WrVqldO4cePGqVu3bvr+++917733qlevXkpLSyv0ewAAAADX4pRIXJF9+/bJsizVq1cvzzGJiYnavn27Dhw4oNDQUEnSggUL1LBhQ23evFnNmjWTJNntds2ZM0cVKlRQgwYN1KpVK+3evVufffaZ3NzcVLduXU2ePFmrVq1SRESEY/99+vRRt27dJEkjRoxQZGSknnvuOUVHR0uSBg4cqD59+hT6tTVq1EhjxoyRJNWpU0czZsxQYmKi7rnnngvGLly4UMePH9fmzZtVsWJFSVLt2rUd2xs3bqzGjRs7lidMmKAlS5Zo2bJliouLU8WKFeXu7q4KFSpc8lTLl19+Wb1799aTTz4pSY5ZzJdfflmtWrVyjOvdu7d69uwpSXrhhRc0ffp0bdq0SW3bti30+wAAAADXYYYNV8SyrHzH7Nq1S6GhoY6yJkkNGjSQv7+/du3a5VhXs2ZNVahQwbEcGBioBg0ayM3NzWndsWPHnPbfqFEjp+2SFB4e7rTuzJkzyszMLMQrc96vJAUHB19w7HOSk5N18803O8ra3506dUpDhw5V/fr15e/vr/Lly2vXrl2OGbaC2rVrl2677TandbfddpvT+/j37D4+PvL19c0zOwAAAMzFDBuuSJ06dWSz2YrkxiJly5Z1WrbZbBddd+40w4s979y1Xxdbd+55bm5uFxTNnJycAuX5+7HPye/GIEOHDlVCQoJefvll1a5dW97e3urSpYuys7Mv+bzLVZjsAAAAMBczbLgiFStWVHR0tGbOnKmsrKwLtqenp6t+/fo6fPiwDh8+7Fj/ww8/KD09XQ0aNCjOuJKkKlWqXHCNWHJy8hXts1GjRkpOTs7zOrG1a9eqd+/euv/++xUeHq6goCAdPHjQaYyHh4dyc3MveZz69etr7dq1F+zbFe8jAAAArj4KG67YzJkzlZubq+bNm+ujjz7S3r17tWvXLk2fPl2RkZGKiopSeHi4evXqpaSkJG3atEmPPPKI7rrrLjVt2rTY8959993asmWLFixYoL1792rMmDHasWPHFe2zZ8+eCgoKUqdOnbR27Vr99NNP+uijj7R+/XpJf81Efvzxx0pOTtZ3332nBx988IIZr5o1a+rrr7/WkSNHdOLEiYseZ9iwYZo3b55mzZqlvXv3asqUKfr44481dOjQK8oPAAAAM3FKpOnGZrg6Qb5q1aqlpKQkTZw4UU8//bRSUlJUpUoVNWnSRLNmzZLNZtMnn3yiAQMG6M4775Sbm5vatm2r1157zSV5o6Oj9dxzz2n48OE6c+aM+vbtq0ceeUTbt2+/7H16eHjoiy++0NNPP617771Xf/75pxo0aKCZM2dKkqZMmaK+ffuqRYsWqly5skaMGHHBNXXjx4/X448/ruuvv15nz5696PWBnTp10rRp0/Tyyy9r4MCBCgsL09y5c52+kgAAAADXDptVkLtGoEhkZmbKz89PGRkZ8vX1ddp25swZHThwQGFhYfLy8nJRQpRG/OwBwDVqrF8Bxpj/D8PAtepS3eB8nBIJAAAAAIaisAEAAACAoShsAAAAAGAoChsAAAAAGIrCBgAAAACGorABAAAAgKEobAAAAABgKAobAAAAABiKwgYAAAAAhirj6gC4tPD54cV6vO0x24v1eAAAAADyxgwbrljv3r1ls9kueOzbt8/V0QAAAIASjRk2FIm2bdtq7ty5TuuqVKnitJydnS0PD4/ijAUAAACUaMywoUh4enoqKCjI6dG6dWvFxcVp0KBBqly5sqKjoyVJO3bsULt27VS+fHkFBgbq4Ycf1okTJxz7stvtmjRpksLCwuTt7a3GjRvrww8/dNVLAwAAAFyGwoarav78+fLw8NDatWs1e/Zspaen6+6779bNN9+sLVu2aMWKFTp69Ki6devmeM6kSZO0YMECzZ49Wzt37tTgwYP10EMPac2aNS58JQAAAEDx45RIFInly5erfPnyjuV27dpJkurUqaP4+HjH+ueff14333yzXnjhBce6OXPmKDQ0VHv27FGNGjX0wgsv6Msvv1RkZKQkqVatWvr222/1xhtv6K677iqmVwQAAAC4HoUNRaJVq1aaNWuWY9nHx0c9e/ZUkyZNnMZ99913WrVqlVO5O2f//v3KycnR6dOndc899zhty87O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WerTp48jU35ZAAAAAKAoubSwzZo1S5LUsmVLp/Vz585V7969JUmvvvqq3Nzc1LlzZ509e1bR0dF6/fXXHWPd3d21fPlyPfHEE4qMjJSPj49iYmI0fvx4x5iwsDB9+umnGjx4sKZNm6Zq1arprbfeUnR0tGNM9+7ddfz4cY0ePVqpqam66aabtGLFCqcbkeSXBQAAAACKkksLW0G+As7Ly0szZ87UzJkz8xxTo0YNffbZZ5fcT8uWLbVt27ZLjomLi1NcXNwVZQEAAACAomLMXSIBAAAAAM4obAAAAABgKAobAAAAABiKwgYAAAAAhqKwAQAAAIChKGwAAAAAYCgKGwAAAAAYisIGAAAAAIaisAEAAACAoShsAAAAAGAoChsAAAAAGIrCBgAAAACGorABAAAAgKEobAAAAABgKAobAAAAABiKwgYAAAAAhqKwAQAAAIChKGwAAAAAYCgKGwAAAAAYisIGAAAAAIaisAEAAACAoShsAAAAAGAoChsAAAAAGIrCBgAAAACGorABAAAAgKEobAAAAABgKAobAAAAABiKwgYAAAAAhqKwAQAAAIChKGwAAAAAYCgKGwAAAAAYisIGAAAAAIaisAEAAACAoShsAAAAAGAoChsAAAAAGIrCBgAAAACGorABAAAAgKEobAAAAABgKAobAAAAABiKwgYAAAAAhqKwAQAAAIChKGwAAAAAYCgKGwAAAAAYisIGAAAAAIaisAEAAACAoShsAAAAAGAoChsAAAAAGIrCBgAAAACGorABAAAAgKEobAAAAABgKAobAAAAABiKwgYAAAAAhqKwAQAAAIChKGwAAAAAYCgKGwAAAAAYisIGAAAAAIaisAEAAACAoShsAAAAAGAoChsAAAAAGIrCBgAAAACGorABAAAAgKEobAAAAABgKAobAAAAABiKwgYAAAAAhqKwAQAAAIChKGwAAAAAYCgKGwAAAAAYisIGAAAAAIaisAEAAACAoShsAAAAAGAoChsAAAAAGIrCBgAAAACGorABAAAAgKEobAAAAABgKAobAAAAABiKwgYAAAAAhqKwAQAAAIChKGwAAAAAYCgKGwAAAAAYisIGAAAAAIaisAEAAACAoShsAAAAAGAoChsAAAAAGIrCBgAAAACGorABAAAAgKEobAAAAABgKAobAAAAABiKwgYAAAAAhqKwAQAAAIChKGwA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9iqhsKFAFi1apCFDhmjMmDFKSkpS48aNFR0drWPHjrk6Wp6ysrLUuHFjzZw509VRCmzNmjWKjY3Vhg0blJCQoJycHLVp00ZZWVmujpanatWq6cUXX9TWrVu1ZcsW3X333erYsaN27tzp6mgFsnnzZr3xxhtq1KiRq6Pkq2HDhkpJSXE8vv32W1dHuqTff/9dt912m8qWLavPP/9cP/zwg1555RVdd911ro52SZs3b3Z6nxMSEiRJXbt2dXGyvE2ePFmzZs3SjBkztGvXLk2ePFnx8fF67bXXXB3tkh599FElJCTonXfe0fbt29WmTRtFRUXpyJEjro4mKf/Pkfj4eE2fPl2zZ8/Wxo0b5ePjo+joaJ05c6aYk/6//DJnZWXp9ttv1+TJk4s5Wd4ulfn06dNKSkrSc889p6SkJH388cfavXu37rvvPhckdZbfe33DDTdoxowZ2r59u7799lvVrFlTbdq00fHjx4s56f8r6N+NlixZog0bNigkJKSYkuWtIJnbtm3r9Hv7vffeK8aExcACCqB58+ZWbGysYzk3N9cKCQmxJk2a5MJUBSfJWrJkiatjFNqxY8csSdaaNWtcHaVQrrvuOuutt95ydYx8nTx50qpTp46VkJBg3XXXXdbAgQNdHSlPY8aMsRo3buzqGIUyYsQI6/bbb3d1jCs2cOBA6/rrr7fsdruro+Spffv2Vt++fZ3WPfDAA1avXr1clCh/p0+fttzd3a3ly5c7rb/lllusZ5991kWp8vb3zxG73W4FBQVZL730kmNdenq65enpab333nsuSHihS332HThwwJJkbdu2rVgz5acgn9ebNm2yJFk///xz8YQqgILkzsjIsCRZX375ZfGEykdemX/55ReratWq1o4dO6waNWpYr776arFny8vFMsfExFgdO3Z0SZ7iwgwb8pWdna2tW7cqKirKsc7NzU1RUVFav369C5Nd+zIyMiRJFStWdHGSgsnNzdX777+vrKwsRUZGujpOvmJjY9W+fXunn22T7d27VyEhIapVq5Z69eqlQ4cOuTrSJS1btkxNmzZV165dFRAQoJtvvln//ve/XR2rULKzs/Wf//xHffv2lc1mc3WcPLVo0UKJiYnas2ePJOm7777Tt99+q3bt2rk4Wd7+/PNP5ebmysvLy2m9t7e38bPHknTgwAGlpqY6/f7w8/NTREQEn41X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",
      "text/plain": [
       "<Figure size 1000x800 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 设置展示图大小\n",
    "fig, ax = plt.subplots(figsize=(10,8))\n",
    "\n",
    "x = np.arange(len(rank_ids)) # the label locations\n",
    "\n",
    "rects1 = ax.bar(x - width, compute_time, width, label='Computing')\n",
    "rects2 = ax.bar(x, communication_time, width, label='Communication')\n",
    "rects3 = ax.bar(x + width, free_time, width, label='Free')\n",
    "\n",
    "\n",
    "# Add some text for labels, title and custom x-axis tick labels, etc.\n",
    "ax.set_ylabel('Time(us)')\n",
    "ax.set_xlabel('Rank ID')\n",
    "ax.set_title('Step Time')\n",
    "ax.set_xticks(x)\n",
    "ax.set_xticklabels(rank_ids)\n",
    "ax.legend()\n",
    "print(words)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3511befaff513e8e",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "source": [
    "## 2）识别通信链路慢"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "2a1e617d2a117125",
   "metadata": {
    "collapsed": false,
    "jupyter": {
     "outputs_hidden": false
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[INFO]Cluster has been analyzed because of the existence of cluster analysis output directory.\n",
      "[INFO]Skip Cluster analyze backend.\n"
     ]
    }
   ],
   "source": [
    "dataset = interface.get_data('cluster', 'slow link')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "c8bca314-a8da-4a5b-985a-c36f00154552",
   "metadata": {},
   "outputs": [],
   "source": [
    "# EDIT THE DATA TO SHOW WHAT YOU WANT\n",
    "data = dataset.get('data')\n",
    "words = dataset.get('bottleneck')\n",
    "rank_ids = list(data.keys())\n",
    "# 柱状图显示属性\n",
    "sdma_bw = [data.get(key, {}).get(\"SDMA bandwidth(GB/s)\") for key in rank_ids]\n",
    "rdma_bw = [data.get(key, {}).get(\"RDMA bandwidth(GB/s)\") for key in rank_ids]\n",
    "# 柱宽\n",
    "width = 0.4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "99ef04c9-ec07-4790-bbb6-0de9bf6c99d0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RDMA bandwidth(GB/s): \n",
      "The average is 0.041, while the maximum  is 0.041GB/s and the minimum is 0.041GB/s. the difference is 0.0GB/s. \n",
      "SDMA bandwidth(GB/s): \n",
      "The average is 0.054, while the maximum  is 0.056GB/s and the minimum is 0.052GB/s. the difference is 0.003GB/s. \n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkAAAAHHCAYAAABXx+fLAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8g+/7EAAAACXBIWXMAAA9hAAAPYQGoP6dpAABMFklEQVR4nO3df3zO9f7H8ee1zTYbmzG2EebXMT9mQkR+1LGMo1gyWmHmR6ei1ApxMHJqpS9RnKRjqJNIySmJM8s6ZMhmRSGVzK8xi43Nz+3z/aOb63S1YT+uaxufx/12u27t+nze1/v1+lzs8uzz67IYhmEIAADARJwqugEAAIDyRgACAACmQwACAACmQwACAACmQwACAACmQwACAACmQwACAACmQwACAACmQwACAACmQwACgAqWlJQki8WipKSkMs9lsVg0ffr0G46bPn26LBZLsea8OvbUqVNl7A6oPAhAQCVmsViK9bDHP5w3q5deeklr1qwp1thffvml0Hvn5eWltm3bav78+crPz3dss5VcSd5L4GbnUtENALi2d9991+b5O++8o4SEhELLW7RoUZ5tVSovvfSSBg4cqPDw8GK/JjIyUn/5y18kSdnZ2Vq3bp2efPJJHTp0SK+++qqDOi0f58+fl4tL6T7aS/NeAjcrAhBQiQ0ZMsTm+bZt25SQkFBo+R/l5eXJw8PDka1VKMMwdOHCBVWtWrVUr2/Xrp3Ne/jEE0+oU6dOWr58+U0fgNzd3Su6BeCmwCEw4CZ39913q3Xr1kpJSVH37t3l4eGhyZMnS5L+/e9/q2/fvqpbt67c3NzUpEkTzZw5s9ChnqtzfP/997rnnnvk4eGhevXqadasWYXqvfHGG2rVqpU8PDzk4+OjDh06aPny5db1V88X2bdvnwYNGiQvLy/VqlVL48aN04ULF2zmunLlimbOnKkmTZrIzc1NgYGBmjx5si5evGgzLjAwUPfdd582bNigDh06qGrVqnrrrbdksViUm5urZcuWWQ9pDR8+vMTvocVikZ+fX6E9J454/44cOaLw8HB5enqqTp06euaZZwpt7+uvvy5nZ2edOXPGumz27NmyWCyKiYmxLsvPz1f16tU1ceJEm2354zlAW7Zs0R133CF3d3c1adJEb731VpHvwY3eyzNnzmj48OGqUaOGvL29FR0drby8vCLfU6CyYw8QcAvIyspSnz599NBDD2nIkCHy8/OTJC1dulTVqlVTTEyMqlWrpi+++ELTpk1TTk5OoT0dp0+fVu/evTVgwAANGjRIH374oSZOnKjg4GD16dNHkvT222/rqaee0sCBA62B5ttvv9X27dv18MMP28w3aNAgBQYGKi4uTtu2bdPrr7+u06dP65133rGOGTVqlJYtW6aBAwfq2Wef1fbt2xUXF6e9e/fq448/tplv//79ioyM1F//+leNHj1azZs317vvvqtRo0apY8eOevTRRyVJTZo0ueH7lZeXZz2hNycnR59//rnWr1+vSZMm2Yyz9/t3/vx59ezZU+np6XrqqadUt25dvfvuu/riiy9s5urWrZsKCgq0ZcsW3XfffZKkzZs3y8nJSZs3b7aO27Vrl86dO6fu3btfc1t3796tXr16qXbt2po+fbquXLmi2NhY69+Rq4rzXg4aNEiNGjVSXFycUlNT9c9//lN16tTRK6+8csP3HKh0DAA3jTFjxhh//LXt0aOHIclYuHBhofF5eXmFlv31r381PDw8jAsXLhSa45133rEuu3jxouHv7288+OCD1mX9+/c3WrVqdd0eY2NjDUlGv379bJY/8cQThiTjm2++MQzDMNLS0gxJxqhRo2zGPffcc4Yk44svvrAua9iwoSHJWL9+faF6np6eRlRU1HV7uurgwYOGpCIfjz/+uFFQUGAz3t7v39y5cw1JxgcffGBdlpubazRt2tSQZGzatMkwDMPIz883vLy8jAkTJhiGYRgFBQVGrVq1jIiICMPZ2dk4e/asYRiGMWfOHMPJyck4ffq0dT5JRmxsrPV5eHi44e7ubhw6dMi67PvvvzecnZ0L/V261nt59c90xIgRNssfeOABo1atWoXGAzcDDoEBtwA3NzdFR0cXWv77c2TOnj2rU6dOqVu3bsrLy9O+fftsxlarVs3mvBhXV1d17NhRP//8s3VZjRo1dOTIEX399dc37GnMmDE2z5988klJ0rp162z++/tDOpL07LPPSpI+++wzm+WNGjVSWFjYDesWx6OPPqqEhAQlJCToo48+0pgxY/TWW28V6sXe79+6desUEBCggQMHWpd5eHhY97hc5eTkpC5duui///2vJGnv3r3KysrS888/L8MwlJycLOm3vUKtW7dWjRo1itzO/Px8bdiwQeHh4WrQoIF1eYsWLUr1Xj722GM2z7t166asrCzl5OSUeC6gohGAgFtAvXr15OrqWmj5d999pwceeEDe3t7y8vJS7dq1rf9IZ2dn24y97bbbCt0XxsfHR6dPn7Y+nzhxoqpVq6aOHTuqWbNmGjNmjL766qsie2rWrJnN8yZNmsjJyUm//PKLJOnQoUNycnJS06ZNbcb5+/urRo0aOnTokM3yRo0aXecdKJlmzZopNDRUoaGhGjBggObPn68nnnhCc+fO1e7du63j7P3+HTp0SE2bNi00rnnz5oV67Natm1JSUnT+/Hlt3rxZAQEBateunUJCQqyHwbZs2aJu3bpdczszMzN1/vz5Qn8W16p5I78PUdJv2yfJZhuBmwUBCLgFFHU11JkzZ9SjRw998803euGFF/Tpp58qISHBer5GQUGBzXhnZ+ci5zYMw/pzixYttH//fq1YsUJdu3bVRx99pK5duyo2NvaGPV7rpnvFvRlfaa/4Kq6ePXtKknWviyPev5Lo2rWrLl++rOTkZG3evNkadLp166bNmzdr3759yszMvG4Asjd7byNQkTgJGrhFJSUlKSsrS6tXr7Y5SfbgwYNlmtfT01ODBw/W4MGDdenSJQ0YMEAvvviiJk2aZHMJ9oEDB2z22vz4448qKChQYGCgJKlhw4YqKCjQgQMHbO5jdOLECZ05c0YNGzYsVj/FDVA3cuXKFUnSuXPnJDnm/WvYsKH27NkjwzBs+t6/f3+hsR07dpSrq6s2b96szZs3a/z48ZKk7t276+2331ZiYqL1+bXUrl1bVatW1YEDBwqtK6qmvd5L4GbAHiDgFnX1/9Z//3/nly5d0j/+8Y9Sz5mVlWXz3NXVVS1btpRhGLp8+bLNugULFtg8f+ONNyTJekXU1RsRzp0712bcnDlzJEl9+/YtVk+enp42l4uX1qeffipJCgkJkeSY9+8vf/mLjh07pg8//NC6LC8vT4sWLSo01t3dXXfccYfef/99paen2+wBOn/+vF5//XU1adJEAQEB16zn7OyssLAwrVmzRunp6dble/fu1YYNGwqNt9d7CdwM2AME3KK6dOkiHx8fRUVF6amnnpLFYtG7775bpsMVvXr1kr+/v+666y75+flp7969mj9/vvr27avq1avbjD148KD69eun3r17Kzk5Wf/617/08MMPWwNGSEiIoqKitGjRIuvhph07dmjZsmUKDw/XPffcU6ye2rdv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HkbLyMgocnxGRkaR4y9cuKCJEycqMjJSXl5eRY6Ji4vTjBkzit03AAC4uZU4AEVFRdk8HzJkSKExw4YNK31HdnT58mUNGjRIhmHozTffvOa4SZMmKSYmxvo8JydH9evXL48WAQBABShxAFqyZIndivv6+srZ2VknTpywWX7ixAnreUZ/5O/vX6zxV8PPoUOH9MUXX1xz748kubm58fUdAACYSKlvhGgPrq6uat++vfUrNSSpoKBAiYmJ6ty5c5Gv6dy5s814SUpISLAZfzX8HDhwQBs3blStWrUcswEAAOCmVOqvwrCXmJgYRUVFqUOHDurYsaPmzp2r3NxcRUdHS/rtcFq9evUUFxcnSRo3bpx69Oih2bNnq2/fvlqxYoV27typRYsWSfot/AwcOFCpqalau3at8vPzrecH1axZU66urhWzoQAAoNKo8AA0ePBgZWZmatq0acrIyFDbtm21fv1664nO6enpcnL6346qLl26aPny5ZoyZYomT56sZs2aac2aNWrdurUk6ejRo/rkk08kSW3btrWptWnTJt19993lsl0AAKDyqvAAJEljx47V2LFji1yXlJRUaFlERIQiIiKKHB8YGCjDMOzZHgAAuMVU6DlAAAAAFYEABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATIcABAAATKfCA9CCBQsUGBgod3d3derUSTt27Lju+FWrVikoKEju7u4KDg7WunXrbNavXr1avXr1Uq1atWSxWJSWlubA7gEAwM2oQgPQypUrFRMTo9jYWKWmpiokJERhYWE6efJkkeO3bt2qyMhIjRw5Urt27VJ4eLjCw8O1Z88e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USZOnGh07dq1XGv+0bhx44wmTZoYBQUFDpm/b9++xogRI2yWDRgwwHjkkUccUi8vL89wdnY21q5da7O8Xbt2xt/+9je71/vj73tBQYHh7+9vvPrqq9ZlZ86cMdzc3Iz333/fITV/7+DBg4YkY9euXXapdaN6V+3YscOQZBw6dKjcamZnZxuSjI0bNzqs3pEjR4x69eoZe/bsMRo2bGi89tprZa51vZpRUVFG//797VbDkdgDVMlcunRJKSkpCg0NtS5zcnJSaGiokpOTK7Azx8rOzpYk1axZ0+G18vPztWLFCuXm5lq/Q85RxowZo759+9r8eTrSgQMHVLduXTVu3FiPPPKI0tPTHVrvk08+UYcOHRQREaE6dero9ttv19tvv+3Qmr936dIl/etf/9KIESMc9sXFXbp0UWJion744QdJ0jfffKMtW7aoT58+Dql35coV5efnF7pzfdWqVR2+R0+SDh48qIyMDJu/s97e3urUqdMt/xlksVjK7XsgL126pEWLFsnb21shISEOqVFQUKChQ4dq/PjxatWqlUNqFCUpKUl16tRR8+bN9fjjjysrK6vcapdEpbgTNP7n1KlTys/Pt34VyFV+fn7at29fBXXlWAUFBXr66ad111132Xylib3t3r1bnTt31oULF1StWjV9/PHHatmypcPqrVixQqmpqXY9d+N6OnXqpKVLl6p58+Y6fvy4ZsyYoW7dumnPnj2qXr26Q2r+/PPPevPNNxUTE6PJkyfr66+/1lNPPSVXV1dFRUU5pObvrVmzRmfOnNHw4cMdVuP5559XTk6OgoKC5OzsrPz8fL344ot65JFHHFKvevXq6ty5s2bOnKkWLVrIz89P77//vpKTk9W0aVOH1Py9jIwMSSryM+jqulvNhQsXNHHiREVGRjr8izzXrl2rhx56SHl5eQoICFBCQoJ8fX0dUuuVV16Ri4uLnnrqKYfMX5TevXtrwIABatSokX766SdNnjxZffr0UXJyspydncutj+IgAKHCjRkzRnv27HH4/902b95caWlpys7O1ocffqioqCh9+eWXDglBhw8f1rhx45SQkFBu30H3+z0Sbdq0UadOndSwYUN98MEHGjlypENqFhQUqEOHDnrppZckSbfffrv27NmjhQsXlksAWrx4sfr06WOXcxuu5YMPPtB7772n5cuXq1WrVkpLS9PTTz+tunXrOmwb3333XY0YMUL16tWTs7Oz2rVrp8jISKWkpDiknpldvnxZgwYNkmEYevPNNx1e75577lFaWppOnTqlt99+W4MGDdL27dtVp04du9ZJSUnRvHnzlJqa6rC9o0V56KGHrD8HBwerTZs2atKkiZKSktSzZ89y66M4OARWyfj6+srZ2VknTpywWX7ixAn5+/tXUFeOM3bsWK1du1abNm3Sbbfd5tBarq6uatq0qdq3b6+4uDiFhIRo3rx5DqmVkpKikydPql27dnJxcZGLi4u+/PJLvf7663JxcVF+fr5D6v5ejRo19Kc//Uk//vijw2oEBAQUCpAtWrRw+KE3STp06JA2btyoUaNGObTO+PHj9fzzz+uhhx5ScHCwhg4dqmeeeUZxcXEOq9mkSRN9+eWXOnfunA4fPqwdO3bo8uXLaty4scNqXnX1c8YMn0FXw8+hQ4eUkJDg8L0/kuTp6ammTZvqzjvv1OLFi+Xi4qLFixfbvc7mzZt18uRJNWjQwPoZdOjQIT377LMKDAy0e71rady4sXx9fR36OVRaBKBKxtXVVe3bt1diYqJ1WUFBgRITEx1+vkp5MgxDY8eO1ccff6wvvvhCjRo1KvceCgoKdPHiRYfM3bNnT+3evVtpaWnWR4cOHfTII48oLS2tXHYFnzt3Tj/99JMCAgIcVuOuu+4qdPuCH374QQ0bNnRYzauWLFmiOnXqqG/fvg6tk5eXJycn249KZ2dnFRQUOLSu9Ns/lgEBATp9+rQ2bNig/v37O7xmo0aN5O/vb/MZlJOTo+3bt99Sn0FXw8+BAwe0ceNG1apVq0L6cNTn0NChQ/Xtt9/afAbVrVtX48eP14YNG+xe71qOHDmirKwsh34OlRaHwCqhmJgYRUVFqUOHDurYsaPmzp2r3NxcRUdHO6zmuXPnbBL6wYMHlZaWppo1a6pBgwZ2rzdmzBgtX75c//73v1W9enXruQXe3t6qWrWq3etNmjRJffr0UYMGDXT27FktX75cSUlJDvsgqF69eqHzmTw9PVWrVi2Hnef03HPP6f7771fDhg117NgxxcbGytnZWZGRkQ6pJ0nPPPOMunTpopdeekmDBg3Sjh07tGjRIi1atMhhNaXf/tFYsmSJoqKi5OLi2I+x+++/Xy+++KIaNGigVq1aadeuXZozZ45GjBjhsJobNmyQYRhq3ry5fvzxR40fP15BQUF2+wy40e/7008/rb///e9q1qyZGjVqpKlTp6pu3boKDw93WM1ff/1V6enp1nvxXA3W/v7+pdrzdL16AQEBGjhwoFJTU7V27Vrl5+dbP4Nq1qwpV1dXu29jrVq19OKLL6pfv34KCAjQqVOntGDBAh09erTUt3C40Xv6x1BXpUoV+fv7q3nz5qWqd6OaNWvW1IwZM/Tggw/K399fP/30kyZMmKCmTZsqLCys1DUdpoKvQsM1vPHGG0aDBg0MV1dXo2PHjsa2bdscWm/Tpk2GpEKPqKgoh9QrqpYkY8mSJQ6pN2LECKNhw4aGq6urUbt2baNnz57Gf/7zH4fUuhZHXwY/ePBgIyAgwHB1dTXq1atnDB482Pjxxx8dVu+qTz/91GjdurXh5uZmBAUFGYsWLXJ4zQ0bNhiSjP379zu8Vk5OjjFu3DijQYMGhru7u9G4cWPjb3/7m3Hx4kWH1Vy5cqXRuHFjw9XV1fD39zfGjBljnDlzxm7z3+j3vaCgwJg6darh5+dnuLm5GT179izze32jmkuWLClyfWxsrN3rXb3UvqjHpk2bHLKN58+fNx544AGjbt26hqurqxEQEGD069fP2LFjh0PqFcUel8Ffr2ZeXp7Rq1cvo3bt2kaVKlWMhg0bGqNHjzYyMjLKVNNRLIbhwNuZAgAAVEKcAwQAAEyHAAQAAEyHAAQAAEyHAAQAAEyHAAQAAEyHAAQAAEyHAAQAAEyHAATAlCwWi9asWVPRbQCoIAQgAJXG8OHDZbFYZLFYVKVKFTVq1EgTJkzQhQsXKro1LV26VDVq1LB5frVXZ2dn+fj4qFOnTnrhhReUnZ1dcY0CKBYCEIBKpXfv3jp+/Lh+/vlnvfbaa3rrrbcUGxtb0W0VycvLS8ePH9eRI0e0detWPfroo3rnnXfUtm1b63daAaicCEAAKhU3Nzf5+/urfv36Cg8PV2hoqBISEqzrs7KyFBkZqXr16snDw0PBwcF6//33bea4++679dRTT2nChAmqWbOm/P39NX369OvWjY2NVUBAgL799tti92qxWOTv76+AgAC1aNFCI0eO1NatW3Xu3DlNmDChRNsNoHwRgABUWnv27NHWrVttvp37woULat++vT777DPt2bNHjz76qIYOHaodO3bYvHbZsmXy9PTU9u3bNWvWLL3wwgs2QeoqwzD05JNP6p133tHmzZvVpk2bMvVcp04dPfLII/rkk0+Un59fprkAOI5LRTcAAL+3du1aVatWTVeuXNHFixfl5OSk+fPnW9fXq1dPzz33nPX5k08+qQ0bNuiDDz5Qx44drcvbtGljPXTWrFkzzZ8/X4mJibr33nutY65cuaIhQ4Zo165d2rJli+rVq2eXbQgKCtLZs2eVlZWlOnXq2GVOAPZFAAJQqdxzzz168803lZubq9dee00uLi568MEHrevz8/P10ksv6YMPPtDRo0d16dIlXbx4UR4eHjbz/HFPTkBAgE6ePGmz7JlnnpGbm5u2bdsmX19fu22DYRiSfjtEBqBy4hAYgErF09NTTZs2VUhIiOLj47V9+3YtXrzYuv7VV1/VvHnzNHHiRG3atElpaWkKCwvTpUuXbOapUqWKzXOLxaKCggKbZffee6+OHj2qDRs22HUb9u7dKy8vL9WqVcuu8wKwHwIQgErLyclJkydP1pQpU3T+/HlJ0ldffaX+/ftryJAhCgkJUePGjfXDDz+Uav5+/fpp+fLlGjVqlFasWGGXnk+ePKnly5crPDxcTk58xAKVFb+dACq1iIgIOTs7a8GCBZJ+O58nISFBW7du1d69e/XXv/5VJ06cKPX8DzzwgN59911FR0frww8/LNFrDcNQRkaGjh8/rr179yo+Pl5dunSRt7e3Xn755VL3BMDxOAcIQKXm4uKisWPHatasWXr88cc1ZcoU/fzzzwoLC5OHh4ceffRRhYeHl+nmgwMHDlRBQYGGDh0qJycnDRgwoFivy8nJUUBAgCwWi7y8vNS8eXNFRUVp3Lhx8vLyKnU/ABzPYlw9Ww8AAMAkOAQGAABMhwAEAABMhwAEAABMhwAEAABMhwAEAABMhwAEAABMhwAEAABMhwAEAABMhwAEAABMhwAEAABMhwAEAABMhwAEAABM5/8BtX0lvKMuBToAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 设置展示图大小\n",
    "fig, ax = plt.subplots(figsize=(10,8))\n",
    "\n",
    "x = np.arange(len(rank_ids)) # the label locations\n",
    "\n",
    "rects1 = ax.bar(x - width/2, sdma_bw, width, label='SDMA')\n",
    "rects2 = ax.bar(x + width/2, rdma_bw, width, label='RDMA')\n",
    "\n",
    "# Add some text for labels, title and custom x-axis tick labels, etc.\n",
    "ax.set_ylabel('Bandwidth(GB/s)')\n",
    "ax.set_xlabel('Rank ID')\n",
    "ax.set_title('Transport Bandwidth')\n",
    "ax.set_xticks(x)\n",
    "ax.set_xticklabels(rank_ids)\n",
    "ax.legend()\n",
    "print(words)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "77d6efa1-48e3-409f-82c4-3e2b3d868898",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RDMA bandwidth(GB/s): \n",
      "The average is 0.041, while the maximum  is 0.041GB/s and the minimum is 0.041GB/s. the difference is 0.0GB/s. \n",
      "SDMA bandwidth(GB/s): \n",
      "The average is 0.054, while the maximum  is 0.056GB/s and the minimum is 0.052GB/s. the difference is 0.003GB/s. \n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(dataset.get('bottleneck'))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ce27a1d3-1354-45f7-88d8-dcb8e438b2b2",
   "metadata": {},
   "source": [
    "## 3) 分布式卡上的kernel算子统计展示"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "e05774e9-c47e-400f-8421-b4b71bcdcbc4",
   "metadata": {},
   "outputs": [],
   "source": [
    "dataset = interface.get_data('cluster', 'kernel')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "e95b6849-1738-4975-929f-734edff5d1c1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>rank id</th>\n",
       "      <th>Name</th>\n",
       "      <th>Input Shapes</th>\n",
       "      <th>Input Data Types</th>\n",
       "      <th>Output Shapes</th>\n",
       "      <th>Duration(us)_mean</th>\n",
       "      <th>Duration(us)_var</th>\n",
       "      <th>Duration(us)_max</th>\n",
       "      <th>Duration(us)_min</th>\n",
       "      <th>Duration(us)_count</th>\n",
       "      <th>Duration(us)_sum</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>0</td>\n",
       "      <td>Add</td>\n",
       "      <td>\"1024,2,5120;1024,2,5120\"</td>\n",
       "      <td>DT_BF16;DT_BF16</td>\n",
       "      <td>\"1024,2,5120\"</td>\n",
       "      <td>45.012050</td>\n",
       "      <td>82.952748</td>\n",
       "      <td>55.9255</td>\n",
       "      <td>35.3108</td>\n",
       "      <td>16</td>\n",
       "      <td>720.1928</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>0</td>\n",
       "      <td>Add</td>\n",
       "      <td>\"2,8192,5120;2,8192,5120\"</td>\n",
       "      <td>DT_BF16;DT_BF16</td>\n",
       "      <td>\"2,8192,5120\"</td>\n",
       "      <td>447.183700</td>\n",
       "      <td>NaN</td>\n",
       "      <td>447.1837</td>\n",
       "      <td>447.1837</td>\n",
       "      <td>1</td>\n",
       "      <td>447.1837</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>0</td>\n",
       "      <td>Add</td>\n",
       "      <td>\"8192,2,1920;1920\"</td>\n",
       "      <td>DT_BF16;DT_BF16</td>\n",
       "      <td>\"8192,2,1920\"</td>\n",
       "      <td>54.330850</td>\n",
       "      <td>1.342846</td>\n",
       "      <td>55.2456</td>\n",
       "      <td>52.6463</td>\n",
       "      <td>4</td>\n",
       "      <td>217.3234</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>0</td>\n",
       "      <td>Add</td>\n",
       "      <td>\"8192,2,2560;2560\"</td>\n",
       "      <td>DT_BF16;DT_BF16</td>\n",
       "      <td>\"8192,2,2560\"</td>\n",
       "      <td>75.485375</td>\n",
       "      <td>0.761315</td>\n",
       "      <td>76.2802</td>\n",
       "      <td>74.2407</td>\n",
       "      <td>4</td>\n",
       "      <td>301.9415</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0</td>\n",
       "      <td>Add</td>\n",
       "      <td>\";\"</td>\n",
       "      <td>FLOAT;FLOAT</td>\n",
       "      <td>\"\"</td>\n",
       "      <td>1.200884</td>\n",
       "      <td>0.017257</td>\n",
       "      <td>1.4996</td>\n",
       "      <td>0.9597</td>\n",
       "      <td>50</td>\n",
       "      <td>60.0442</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1441</th>\n",
       "      <td>15</td>\n",
       "      <td>atomic_memset-1_67_1998432_1_0</td>\n",
       "      <td>\"\"</td>\n",
       "      <td>UNDEFINED</td>\n",
       "      <td>\"\"</td>\n",
       "      <td>3.160000</td>\n",
       "      <td>NaN</td>\n",
       "      <td>3.1600</td>\n",
       "      <td>3.1600</td>\n",
       "      <td>1</td>\n",
       "      <td>3.1600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1442</th>\n",
       "      <td>15</td>\n",
       "      <td>trans_Cast_14</td>\n",
       "      <td>\"1\"</td>\n",
       "      <td>FLOAT</td>\n",
       "      <td>\"1\"</td>\n",
       "      <td>1.390000</td>\n",
       "      <td>0.023067</td>\n",
       "      <td>1.6000</td>\n",
       "      <td>1.2600</td>\n",
       "      <td>4</td>\n",
       "      <td>5.5600</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1443</th>\n",
       "      <td>15</td>\n",
       "      <td>trans_Cast_15</td>\n",
       "      <td>\"\"</td>\n",
       "      <td>INT32</td>\n",
       "      <td>\"\"</td>\n",
       "      <td>64.445000</td>\n",
       "      <td>36.276100</td>\n",
       "      <td>70.3000</td>\n",
       "      <td>59.2000</td>\n",
       "      <td>4</td>\n",
       "      <td>257.7800</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1444</th>\n",
       "      <td>15</td>\n",
       "      <td>trans_Cast_4</td>\n",
       "      <td>\"1\"</td>\n",
       "      <td>FLOAT</td>\n",
       "      <td>\"1\"</td>\n",
       "      <td>1.555000</td>\n",
       "      <td>0.035857</td>\n",
       "      <td>1.9400</td>\n",
       "      <td>1.3200</td>\n",
       "      <td>8</td>\n",
       "      <td>12.4400</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1445</th>\n",
       "      <td>15</td>\n",
       "      <td>trans_Cast_5</td>\n",
       "      <td>\"\"</td>\n",
       "      <td>INT32</td>\n",
       "      <td>\"\"</td>\n",
       "      <td>62.895000</td>\n",
       "      <td>15.584200</td>\n",
       "      <td>69.8600</td>\n",
       "      <td>56.7600</td>\n",
       "      <td>8</td>\n",
       "      <td>503.1600</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>1446 rows × 11 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "      rank id                            Name               Input Shapes  \\\n",
       "0           0                             Add  \"1024,2,5120;1024,2,5120\"   \n",
       "1           0                             Add  \"2,8192,5120;2,8192,5120\"   \n",
       "2           0                             Add         \"8192,2,1920;1920\"   \n",
       "3           0                             Add         \"8192,2,2560;2560\"   \n",
       "4           0                             Add                        \";\"   \n",
       "...       ...                             ...                        ...   \n",
       "1441       15  atomic_memset-1_67_1998432_1_0                         \"\"   \n",
       "1442       15                   trans_Cast_14                        \"1\"   \n",
       "1443       15                   trans_Cast_15                         \"\"   \n",
       "1444       15                    trans_Cast_4                        \"1\"   \n",
       "1445       15                    trans_Cast_5                         \"\"   \n",
       "\n",
       "     Input Data Types  Output Shapes  Duration(us)_mean  Duration(us)_var  \\\n",
       "0     DT_BF16;DT_BF16  \"1024,2,5120\"          45.012050         82.952748   \n",
       "1     DT_BF16;DT_BF16  \"2,8192,5120\"         447.183700               NaN   \n",
       "2     DT_BF16;DT_BF16  \"8192,2,1920\"          54.330850          1.342846   \n",
       "3     DT_BF16;DT_BF16  \"8192,2,2560\"          75.485375          0.761315   \n",
       "4         FLOAT;FLOAT             \"\"           1.200884          0.017257   \n",
       "...               ...            ...                ...               ...   \n",
       "1441        UNDEFINED             \"\"           3.160000               NaN   \n",
       "1442            FLOAT            \"1\"           1.390000          0.023067   \n",
       "1443            INT32             \"\"          64.445000         36.276100   \n",
       "1444            FLOAT            \"1\"           1.555000          0.035857   \n",
       "1445            INT32             \"\"          62.895000         15.584200   \n",
       "\n",
       "      Duration(us)_max  Duration(us)_min  Duration(us)_count  Duration(us)_sum  \n",
       "0              55.9255           35.3108                  16          720.1928  \n",
       "1             447.1837          447.1837                   1          447.1837  \n",
       "2              55.2456           52.6463                   4          217.3234  \n",
       "3              76.2802           74.2407                   4          301.9415  \n",
       "4               1.4996            0.9597                  50           60.0442  \n",
       "...                ...               ...                 ...               ...  \n",
       "1441            3.1600            3.1600                   1            3.1600  \n",
       "1442            1.6000            1.2600                   4            5.5600  \n",
       "1443           70.3000           59.2000                   4          257.7800  \n",
       "1444            1.9400            1.3200                   8           12.4400  \n",
       "1445           69.8600           56.7600                   8          503.1600  \n",
       "\n",
       "[1446 rows x 11 columns]"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "27b75df4-792b-43dc-aa5c-d3c265642c1e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 保存到csv查看， 可修改保存路径\n",
    "dataset.to_csv('cluster_kernel_details.csv', index=False, sep='\\t')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e3b0afac-4b79-46a5-bce4-c17ebf690a38",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
